package com.gpb.common.utils;

/**
 * 身份证工具
 * @author 郭彭彬
 * 
	身份证号码的编排规则 
	身份证号码中这些数字是怎样排列的，每个数字表示什么意思呢？下面就让我们一起来探索其中的规律吧！新的身份证号码是由18位数字组成的，它们分别表示： 
	
	（1）前1、2位数字表示：所在省份的代码； 
	（2）第3、4位数字表示：所在城市的代码； 
	（3）第5、6位数字表示：所在区县的代码； 
	（4）第7~14位数字表示：出生年、月、日； 
	（5）第15、16位数字表示：所在地的派出所的代码； 
	（6）第17位数字表示性别：奇数表示男性，偶数表示女性； 
	（7）第18位数字是校检码：也有的说是个人信息码，一般是随计算机的随机产生， 
	看看下面的格式
	
	15位的身份证号  dddddd yymmdd xx p 
	
	18位的身份证号  dddddd yyyymmdd xx p y 
	
	其中
	dddddd为地址码（省地县三级）18位中的和15位中的不完全相同 
	yyyymmdd yymmdd 为出生年月日 
	xx顺号类编码 
	p性别 
	18位中末尾的y为校验码，将前17位的ascii码值经位移、异或运算结果不在0-9的令其为x 
	下面是国家标准
	
	18位身份证标准在国家质量技术监督局于1999年7月1日实施的GB11643-1999《公民身份号码》中做了明确规定。 
	　　GB11643-1999《公民身份号码》为GB11643-1989《社会保障号码》的修订版，其中指出将原标准名称“社会保障号码”更名为“公民身份号码”，另外GB11643-1999《公民身份号码》从实施之日起代替GB11643-1989。 
	　　公民身份号码是特征组合码，由十七位数字本体码和一位校验码组成。排列顺序从左至右依次为：六位数字地址码，八位数字出生日期码，三位数字顺序码和一位校验码。其含义如下： 
	　　1. 地址码：表示编码对象常住户口所在县(市、旗、区)的行政区划代码，按GB/T2260的规定执行。 
	　　2. 出生日期码：表示编码对象出生的年、月、日，按GB/T7408的规定执行，年、月、日分别用4位、2位、2位数字表示，之间不用分隔符。 
	　　3. 顺序码：表示在同一地址码所标识的区域范围内，对同年、同月、同日出生的人编定的顺序号，顺序码的奇数分配给男性，偶数分配给女性。 
	　　校验的计算方式： 
	　　1. 对前17位数字本体码加权求和 
	　　公式为：S = Sum(Ai * Wi), i = 0, ... , 16 
	　　其中Ai表示第i位置上的身份证号码数字值，Wi表示第i位置上的加权因子，其各位对应的值依次为： 7 9 10 5 8 4 2 1 6 3 7 9 10 5 8 4 2 
	　　2. 以11对计算结果取模 
	　　Y = mod(S, 11) 
	　　3. 根据模的值得到对应的校验码 
	　　对应关系为： 
	　　 Y值：     0  1  2  3  4  5  6  7  8  9  10 
	　　校验码： 1  0  X  9  8  7  6  5  4  3   2
	
	看完这些信息，能不能写个程序解决下面这个问题呢。
	
	Q: 任意给出一个18位身份证号码的前17位，然后计算出最后一位校验码
 */
public final class IDNumberUtils {
	//身份证号码的前17位对应加权因子
	private final static int[] wi=new int[]{7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2};
	//值：0,1,2,3,4,5,6,7,8,9,10 对应的效验码
	private final static char[] vc=new char[]{'1','0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'};

	/**
	 * char 转 int
	 * @param c
	 * @return
	 */
	private final static int char2int(char c){
		return (int)(c-'0');
	}
	
	/**
	 * 验证身份证是否合法
	 * @param idNumber
	 * @return
	 */
	public final static boolean verify(String idNumber){
		if(idNumber==null&&idNumber.length()<18){
			return false;
		}
		return idNumber.substring(17).equalsIgnoreCase(mantissa(idNumber)+"");
	}
	/**
	 * 输入身份证17位补全18位
	 * @param idNumber
	 * @return
	 */
	public final static String complement(String idNumber){
		if(idNumber==null&&idNumber.length()<17){
			return idNumber;
		}
		return idNumber.substring(0,17)+mantissa(idNumber);
	}
	/**
	 * 根据前17位计算位数
	 * @return
	 */
	public final static char mantissa(String idNumber){
		//转换为char数组
		char[] caidNs=idNumber.toCharArray();
		//判断输入位数
		if(caidNs.length<17){
			return 0;
		}
		int s=0;
		//对前17位数字本体码加权求和
		for (int i=0;i<17;i++) {
			s+=char2int(caidNs[i])*wi[i];
		}
		//以11对计算结果取模
		int y=s%11;
		//根据模的值得到对应的校验码
		return vc[y];
	}
	public static void main(String[] args) {
		System.out.println(IDNumberUtils.mantissa("350322198803051001"));
		System.out.println(IDNumberUtils.verify("35032219880305109x"));
		System.out.println(IDNumberUtils.complement("350322198803051091"));
	}
}
